// Copyright (c) XiGuan 2023
// Author： xiguan
// Email: xiguan.teng@qq.com
// Create on 2023/11/16
// TODO:
//

#include "HttpRequest.h"

#include <algorithm>
#include <regex>

const std::unordered_set<std::string> HttpRequest::DEFAULT_HTML_{
    "/index", "/register", "/login", "/welcome", "/video", "/picture",
};

const std::unordered_map<std::string, int> HttpRequest::DEFAULT_HTML_TAG_{
    {"/register.html", 0},
    {"/login.html", 1},
};

HttpRequest::HttpRequest() { init(); }

void HttpRequest::init() {
  method_ = "";
  path_ = "";
  version_ = "";
  body_ = "";

  state_ = REQUEST_LINE;
  header_.clear();
  post_.clear();
}

bool HttpRequest::is_keep_alive() const {
  if (header_.count("Connection") == 1) {
    return header_.find("Connection")->second == "keep-alive" &&
           version_ == "1.1";
  }
  return false;
}

bool HttpRequest::parse_request_line_(const std::string &line) {
  std::regex patten("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");
  std::smatch sub_match;

  if (std::regex_match(line, sub_match, patten)) {
    method_ = sub_match[1];
    path_ = sub_match[2];
    version_ = sub_match[3];
    state_ = HEADERS;
    return true;
  }
  LOG_ERROR("Parse request line error.\n");
  return false;
}

void HttpRequest::parse_path_() {
  if (path_ == "/") {
    path_ = "/index.html";
  } else {
    for (auto &it : DEFAULT_HTML_) {
      if (it == path_) {
        path_ += ".html";
        break;
      }
    }
  }
}

bool HttpRequest::parse_request_header_(const std::string &line) {
  std::regex patten("^([^:]*): ?(.*)$");
  std::smatch sub_match;

  if (std::regex_match(line, sub_match, patten)) {
    header_[sub_match[1]] = sub_match[2];
  } else {
    state_ = BODY;
  }
}

bool HttpRequest::parse_request_body_(const std::string &line) {
  body_ = line;
  parse_post();
  state_ = FINISH;
  LOG_DEBUG("body: %s, len: %d.\n", body_.c_str(), line.size());
}

int HttpRequest::conver_hex(char ch) {
  if (ch >= 'A' && ch <= 'F') {
    return ch - 'A' + 10;
  }
  if (ch >= 'a' && ch <= 'f') {
    return ch - 'a' + 10;
  }
  return ch;
}

void HttpRequest::parse_from_urlencoded_() {
  if (body_.size() <= 0) {
    return;
  }
  std::string key;
  std::string value;
  int num = 0;
  int n = body_.size();
  int idx = 0;
  int jdx = 0;

  for (; idx < n; ++idx) {
    auto ch = body_[idx];
    switch (ch) {
      case '=':
        key = body_.substr(jdx, idx - jdx);
        jdx = idx + 1;
        break;
      case '+':
        body_[idx] = ' ';
        break;
      case '%':
        num = conver_hex(body_[idx + 1]) * 16 + conver_hex(body_[idx + 2]);
        body_[idx + 2] = num % 10 + '0';
        body_[idx + 1] = num / 10 + '0';
        idx += 2;
        break;
      case '&':
        value = body_.substr(jdx, idx - jdx);
        jdx = idx + 1;
        post_[key] = value;
        break;
      default:
        break;
    }
  }

  assert(jdx <= idx);
  if (post_.count(key) == 0 && jdx < idx) {
    value = body_.substr(jdx, idx - jdx);
    post_[key] = value;
  }
}

bool HttpRequest::user_verify(const std::string &username,
                              const std::string &passwd, bool is_login) {
  if (username.size() <= 0 || passwd.size() <= 0) {
    return false;
  }
  LOG_INFO("Verify name: %s, password: %s", username.c_str(), passwd.c_str());
  MYSQL *sql = nullptr;
  SqlConnRAII(&sql, SqlConnPool::Instance());
  assert(sql != nullptr);

  return true;
}

void HttpRequest::parse_post() {
  if (method_ == "POST" &&
      header_["Content-Type"] == "application/x-www-form-urlencoded") {
    parse_from_urlencoded_();  // 处理表单数据
    if (DEFAULT_HTML_TAG_.count(path_)) {
      int tag = DEFAULT_HTML_TAG_.find(path_)->second;
      if (tag == 0 || tag == 1) {
        if (user_verify(post_["username"], post_["password"],
                        tag == 0 ? 0 : 1)) {
          path_ = "welcome.html";
        } else {
          path_ = "/error.html";
        }
      }
    }
  }
}

bool HttpRequest::parse(Buffer &buffer) {
  const char CRLF[] = "\r\n";
  if (buffer.ReadableBytes() <= 0) {
    return false;
  }

  while (buffer.ReadableBytes() && state_ != FINISH) {
    const char *line_end =
        std::search(buffer.Peek(), buffer.BeginWriteConst(), CRLF, CRLF + 2);

    std::string line(buffer.Peek(), line_end);
    switch (state_) {
      case REQUEST_LINE:
        if (!parse_request_line_(line)) {
          return false;
        }
        parse_path_();
        break;
      case HEADERS:
        parse_request_header_(line);
        if (buffer.ReadableBytes() <= 2) {
          state_ = FINISH;
        }
        break;
      case BODY:
        parse_request_body_(line);
        break;
      default:
        break;
    }
    if (line_end == buffer.BeginWrite()) {
      break;
    }
    buffer.RetrieveUntil(line_end + 2);
  }
  LOG_DEBUG("[%s], [%s], [%s]", method_.c_str(), path_.c_str(),
            version_.c_str());
  return true;
}

std::string &HttpRequest::getMethod() { return method_; }

std::string &HttpRequest::getPath() { return path_; }

std::string &HttpRequest::path() { return path_; }

std::string &HttpRequest::getVersion() { return version_; }
